package com.sun.mongodb.core.update;

import com.sun.mongodb.core.query.CriteriaWrapper;
import com.sun.mongodb.core.reflection.ReflectionUtil;
import com.sun.mongodb.core.reflection.SerializableFunction;
import lombok.Builder;
import lombok.Data;
import org.bson.Document;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.lang.Nullable;

import java.util.Map;

@Data
@Builder
public class UpdateBuilder extends Update {
    public UpdateBuilder(){

    }

	/**
	 *  $set 指定一个键并更新键值，若键不存在则创建
     *  对于内嵌文档在使用$set更新时，使用"."连接的方式 ==> db.order_info.update({"_id": order_id}, {"$set": {"size.width": 8}})
	 * @param key   键名
	 * @param value 键值
	 * @return
	 */
	public <E, R> UpdateBuilder set(SerializableFunction<E, R> key, Object value) {
		this.set(ReflectionUtil.getFieldName(key),value);
		return this;
	}


	/**
	 * $unset 用于删除键。
     *  不论对目标键使用1、0、-1或者具体的字符串等都是可以删除该目标键  ==> db.order_info.update({"_id": order_id}, {"$unset": {"size.width": 1}})
	 * @param key   键名
	 * @return
	 */
	public <E, R> UpdateBuilder unset(SerializableFunction<E, R> key) {
		this.unset(ReflectionUtil.getFieldName(key));
		return this;
	}

    /**
     * $inc 相加  可以对文档中数字型（只能为满足要求的数字）的键进行增减的操作。
     * 正数增加 , 负数减少
     * @param key   键名
     * @param value  自增值
     * @return
     */
    public <E, R> UpdateBuilder inc(SerializableFunction<E, R> key, Number value) {
        this.inc(ReflectionUtil.getFieldName(key),value);
        return this;
    }

    /**
     *  $mul  相乘
     *  若指定的字段在文档中不存在，则创建字段并且设置值为0作为乘数  相乘 ；
     *  使用点号指定一个内嵌文档或数组字段；
     * @param key
     * @param value  值
     * @return
     */
    public <E, R> UpdateBuilder mul(SerializableFunction<E, R> key, Object value) {
        this.max(ReflectionUtil.getFieldName(key),value);
        return this;
    }

    /**
     *  $max    比较key键值 与 value值的大小,设为大的值；
     *  若字段不存在则创建并设置值为 value
     *  若key对应的oldValue < value,则修改值为 value;
     *  若key对应的oldValue >= value,则无变化
     *  注意数据类型
     *  不同类型的数据比较的时候会使用   BSON比较规则
     * @param key
     * @param value  值
     * @return
     */
    public <E, R> UpdateBuilder max(SerializableFunction<E, R> key, Object value) {
        this.max(ReflectionUtil.getFieldName(key),value);
        return this;
    }

    /**
     *  $min    比较key键值 与 value值的大小,设为小的值；
     *  若字段不存在则创建并设置值为 value
     *  若key对应的oldValue > value,则修改值为 value;
     *  若key对应的oldValue <= value,则无变化
     *  注意数据类型
     *  不同类型的数据比较的时候会使用BSON比较规则
     * @param key
     * @param value  值
     * @return
     */
    public <E, R> UpdateBuilder min(SerializableFunction<E, R> key, Object value) {
        this.min(ReflectionUtil.getFieldName(key),value);
        return this;
    }

    /*----------------------------数组操作-----------------------------*/


	/**
     * 向数组添加元素  不去重
	 * $push 数组修改器   向文档的某个数组类型的键添加一个数组元素(要求键值类型必须是数组)，不过滤重复的数据。键不存在，则创建数组类型的键。
	 * @param key   键名
	 * @param value  push值
	 * @return
	 */
	public <E, R> UpdateBuilder push(SerializableFunction<E, R> key,@Nullable Object value) {
		this.push(ReflectionUtil.getFieldName(key),value);
		return this;
	}

    /**
     * $pushAll 一次可以追加多个值到一个数组字段内。
     * @param key   键名
     * @param value  push值
     * @return
     */
    public <E, R> UpdateBuilder pushAll(SerializableFunction<E, R> key, Object... value) {
        this.pushAll(ReflectionUtil.getFieldName(key), value);
        return this;
    }

    /**
     * 向数组添加元素  去重
     * $addToSet 数组修改器   向文档的某个数组类型的键添加一个数组元素(要求键值类型必须是数组)，
     * 元素已存在则不添加
     * @param key   键名
     * @param value  push值
     * @return
     */
    public <E, R> UpdateBuilder addToSet(SerializableFunction<E, R> key,@Nullable Object value) {
        this.addToSet(ReflectionUtil.getFieldName(key),value);
        return this;
    }



    /**
     * 删除数组第一个元素
     * @param key   键名
     * @return
     */
    public <E, R> UpdateBuilder popFirst(SerializableFunction<E, R> key) {
        this.pop(ReflectionUtil.getFieldName(key),Update.Position.FIRST);
        return this;
    }
    /**
     * 删除数组最后一个元素
     * $pop 删除数组中的第一个或者最后一个元素，给$pop传递-1会删除第一个元素传递1会删除最后一个元素
     * @param key   键名
     * @return
     */
    public <E, R> UpdateBuilder popLast(SerializableFunction<E, R> key) {
        this.pop(key, Position.LAST);
        return this;
    }

    /**
     * $pop 删除数组中的第一个或者最后一个元素，给$pop传递-1会删除第一个元素传递1会删除最后一个元素
     * @param key   键名
     * @return
     */
    public <E, R> UpdateBuilder pop(SerializableFunction<E, R> key,Position position) {
        this.pop(ReflectionUtil.getFieldName(key), position);
        return this;
    }

    /**
     * $pull  删除数组中的第一个或者最后一个元素，给$pop传递-1会删除第一个元素传递1会删除最后一个元素
     * @param key   键名
     * @return
     */
    public <E, R> UpdateBuilder pull(SerializableFunction<E, R> key,Object value) {
        this.pull(ReflectionUtil.getFieldName(key), value);
        return this;
    }

    /**
     *  $pull 删除所有符合条件的 数组 元素
     *  @param wrapper 条件
     */
    public Update pull(CriteriaWrapper wrapper) {
        Document document= wrapper.buildQuery().getQueryObject();
        for (Map.Entry<String, Object> entry : document.entrySet()) {
            this.pull(entry.getKey(),entry.getValue());
        }
        return this;
    }



	/**
	 * $currentDate 设置字段值为当前时间   值类型为  Date
	 * @param key   键名
	 * @return
	 */
	public <E, R> UpdateBuilder currentDate(SerializableFunction<E, R> key) {
		this.currentDate(ReflectionUtil.getFieldName(key));
		return this;
	}

	/**
	 * $currentDate 设置字段值为当前时间   值类型为  Timestamp时间戳类型
	 * @param key   键名
	 * @return
	 */
	public <E, R> UpdateBuilder currentTimestamp(SerializableFunction<E, R> key) {
		this.currentTimestamp(ReflectionUtil.getFieldName(key));
		return this;
	}





}
